Spring Boot + KotlinでSentryことはじめ

Spring Boot + KotlinでSentryことはじめ

SpringBoot + Kotlinで構成されたアプリケーションにSentryを導入する検証を行ったためメモがてら知見を残します。
Clock Icon2023.05.24

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

prismatix事業部の太田です。

Spring Boot + Kotlinで構成されたアプリケーションにSentryを導入する検証を行ったため、メモがてら知見を残します。

バージョンなど

  • Spring Boot 3.1.0
  • Kotlin 1.8.21
  • JDK OpenJDK Runtime Environment Corretto-17.0.7.7.1 (build 17.0.7+7-LTS)

導入

専用のSDKが用意されているので導入します。

また、logging統合のライブラリを追加すると、ログ出力した内容もSentryに送信する設定を自動で行ってくれます。

dependencies {
	implementation("org.springframework.boot:spring-boot-starter-web")
	implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
	implementation("org.jetbrains.kotlin:kotlin-reflect")
	// Sentry SDK 2系の場合は別のライブラリなので注意
	implementation("io.sentry:sentry-spring-boot-starter-jakarta:6.19.1")
        // logback統合
	implementation("io.sentry:sentry-logback:6.19.1")
	
	testImplementation("org.springframework.boot:spring-boot-starter-test")
}

Sentryの管理画面から取得したdsnを設定します。

sentry:
  dsn: ${SENTRY_DSN}

これで送信する準備が整いました。

ExceptionHandlerで捕捉される例外についてもSentryに送信する

上記の設定では、unhandled exception、及びlogbackで出力されたerrorログの内容がSentryに送信されます。 一方で、Spring BootでWebアプリケーションを実装している場合、@ControllerAdvice@ExceptionHandlerを用いた例外ハンドリングを行っていることが多いのではないでしょうか。

以下の設定を追加することで、@ExceptionHandlerによって捕捉される例外についてもSentryに送信することができます。

sentry:
  dsn: ${SENTRY_DSN}
  sentry.exception-resolver-order=-2147483647 // the value of org.springframework.core.Ordered#HIGHEST_PRECEDENCE

Sentryが実装しているHandlerExceptionResolverの優先度を高めることで、ハンドリング順をコントロールしているようです。

実行サンプル

以下のように、@ExceptionHandlerで例外をエラーログ出力に変換した場合と、そのままSpringの領域にthrowした場合のコードで実験してみました。

package com.example.demo

import org.slf4j.LoggerFactory
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.GetMapping

@Controller
class SampleController {

     @GetMapping("/sample/handled")
     fun sampleHandled() {
         throw HandledRuntimeException("handled exception sample")
     }

     @GetMapping("/sample/unhandled")
     fun sampleUnhandled() {
         throw UnhandledRuntimeException("unhandled exception sample")
     }
}

@ControllerAdvice
class GlobalExceptionHandler {

    @ExceptionHandler(HandledRuntimeException::class)
    fun handleRuntimeException(e: HandledRuntimeException) {
        LoggerFactory.getLogger(this::class.java).error("exception handler sample")
    }
}

class HandledRuntimeException(message: String) : RuntimeException(message)

class UnhandledRuntimeException(message: String) : RuntimeException(message)

結果

以下のように、エラーログについてはハンドリングされる順番に関わらずSentryへ送信されましたが、例外についてはハンドリングされる順番によって結果が変化しました。

sentry.exception-resolver-order error log handled exception unhandled exception
最優先(-2147483647) 送信される 送信される 送信される
設定なし 送信される 送信されない 送信される

最後に

この記事ではSpring Bootのよくある実装パターンである@ExceptionHandlerとSentryの関係性について見てきました。

エラー監視は便利ですが、大量に出力されると運用が辛いため、想定外のエラーだけ検知されるようにうまく量をコントロールできる設定にできると良いですね。

参考

https://docs.sentry.io/platforms/java/guides/spring-boot/

https://stackoverflow.com/questions/48401974/how-to-have-my-own-error-handlers-before-sentry-in-a-spring-application

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.